#!/usr/bin/env node
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => {
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
  return value;
};
var cdts_exports = {};
__export(cdts_exports, {
  DtsHelper: () => DtsHelper
});
module.exports = __toCommonJS(cdts_exports);
var ts = __toESM(require("typescript"));
var import_glob = __toESM(require("glob"));
var import_fs_extra = __toESM(require("fs-extra"));
var import_path = __toESM(require("path"));
var import_prettier = __toESM(require("prettier"));
var import_os = __toESM(require("os"));
var import_minimist = __toESM(require("minimist"));
var import_chalk = __toESM(require("chalk"));
var import_child_process = require("child_process");
var import_md5 = __toESM(require("md5"));
var import_dotenv = __toESM(require("dotenv"));
var import_tosrc = require("./mod/tosrc");
var import_package = require("./package.json");
import_dotenv.default.config();
process.on("unhandledRejection", (err) => {
  const stderr = err.stderr;
  if (stderr) {
    return console.log(stderr);
  }
  throw err;
});
const { _: cmdArgs, ...cmdOpt } = (0, import_minimist.default)(process.argv.slice(2));
const debug = (...msg) => {
  console.log(...msg);
  process.exit(1);
};
const isSingleDomain = Boolean(process.env?.CURRENT_DOMAIN);
const _DtsHelper = class {
  constructor(domain) {
    /** 缓存实例 */
    __publicField(this, "cache", null);
    __publicField(this, "domain");
    __publicField(this, "_pkg", null);
    this.domain = domain || process.env?.CURRENT_DOMAIN;
    this.initCache();
    this.getPackageJson();
  }
  /** 缓存绝对路径 */
  get cacheFullPath() {
    return import_path.default.resolve(_DtsHelper.cacheDir, this.cacheName);
  }
  /** 缓存文件名 */
  get cacheName() {
    const dirname = import_path.default.basename(process.cwd());
    return (0, import_md5.default)([dirname, this.domain, "cache"].join("_"));
  }
  /** 缓存目录绝对路径 */
  get cacheDirPath() {
    return import_path.default.dirname(this.cacheFullPath);
  }
  /** 类型声明文件名 */
  get dtsFileName() {
    return `${this.domain}.d.ts`;
  }
  /** 类型声明文件绝对路径 */
  get dtsFilePath() {
    return import_path.default.resolve(this.cacheDirPath, this.dtsFileName);
  }
  getPackageJson() {
    const pkgPath = import_path.default.join(process.cwd(), "package.json");
    if (import_fs_extra.default.existsSync(pkgPath)) {
      this._pkg = import_fs_extra.default.readJsonSync(import_path.default.join(process.cwd(), "package.json"));
    }
  }
  get packageName() {
    return this._pkg.name || "unkown_package_name";
  }
  /** 初始化缓存 */
  initCache() {
    import_fs_extra.default.mkdirpSync(import_path.default.dirname(this.cacheFullPath));
    if (this.cache !== null)
      return this.cache;
    try {
      this.cache = JSON.parse(import_fs_extra.default.readFileSync(this.cacheFullPath, { encoding: "utf-8" }));
    } catch (err) {
      this.cache = {};
    }
    return this.cache;
  }
  /** 异步获取ts,tsx文件信息 */
  async stat() {
    const globPattern = cmdOpt.all ? "src/**/*.{ts,tsx}" : `src/${this.domain}/**/*.{ts,tsx}`;
    const files = import_glob.default.sync(globPattern, { absolute: true });
    const fileStats = await Promise.all(
      files.filter((f) => /\.d\.ts$/.test(f) === false).map((file) => {
        return import_fs_extra.default.stat(file).then((stat) => [file, stat.mtime.getTime().toString()]);
      })
    );
    return fileStats;
  }
  /** 文件差异列表， */
  async diff(useHash = false) {
    const diffFiles = useHash ? this.diffHash() : await this.diffMtime();
    diffFiles.forEach(([file, hash]) => {
      this.cache[file] = { hash };
    });
    return diffFiles;
  }
  /** 比较最后修改时间 */
  async diffMtime() {
    const fileStats = await this.stat();
    const cache = this.cache;
    const diffFiles = fileStats.filter(([file, hash]) => {
      if (!cache[file] || cache[file] && cache[file].hash !== hash) {
        return true;
      }
      return false;
    });
    return diffFiles;
  }
  /** 比较hash */
  diffHash() {
    const cache = this.cache;
    const domainDir = isSingleDomain ? "src" : "src/" + this.domain;
    console.log("domainDir: " + domainDir);
    const shell = `git ls-files -s ${domainDir}| grep -nE ".tsx?$"`;
    const result = (0, import_child_process.execSync)(shell, { stdio: "pipe" }).toString("utf-8");
    const diffFiles = [];
    result.replace(/.+ +(.+?)\s+\d+\s+(.+)/gm, (_, hash, _file) => {
      const file = process.cwd() + "/" + _file;
      if (!cache[file] || cache[file].hash !== hash) {
        diffFiles.push([file, hash]);
      }
      return "";
    });
    return diffFiles;
  }
  updateDts(file, dts) {
    const cacheKey = this.cache[file] ? file : file + "x";
    if (!this.cache[cacheKey]) {
      console.log(file + " can not cached");
      return;
    }
    this.cache[cacheKey].dts = dts;
  }
  save() {
    import_fs_extra.default.writeFileSync(this.cacheFullPath, JSON.stringify(this.cache));
    return this;
  }
  /**
   * 转换为 domain.d.ts
   * @param isPretty 是否格式化
   */
  toDTS(isPretty = true) {
    const output = Object.values(this.cache).map((it) => it.dts).join("\n");
    return isPretty ? import_prettier.default.format(output, { parser: "typescript", printWidth: 120, useTabs: false, tabWidth: 4 }) : output;
  }
  async createSourceFiles(files) {
    const sourceFiles = await Promise.all(
      files.map((file) => {
        return import_fs_extra.default.readFile(file, { encoding: "utf-8" }).then((content) => {
          return [file, ts.createSourceFile(file, content, ts.ScriptTarget.ESNext)];
        });
      })
    );
    return Object.fromEntries(sourceFiles);
  }
  /**
   * Create a compilerHost object to allow the compiler to read and write files
   * @param sourceFiles
   * @param writeFile
   */
  createCompilerHost(sourceFiles) {
    const compilerHost = {
      getSourceFile: (filename) => sourceFiles[filename],
      writeFile: () => "",
      getDefaultLibFileName: () => "lib.d.ts",
      useCaseSensitiveFileNames: () => false,
      getCanonicalFileName: (fileName) => fileName,
      getCurrentDirectory: () => "",
      getNewLine: () => "\n",
      fileExists: (fileName) => !!sourceFiles[fileName],
      readFile: () => "",
      directoryExists: () => true,
      getDirectories: () => []
    };
    return compilerHost;
  }
  async transpile(sourceFiles, writeFile, options) {
    const compilerHost = this.createCompilerHost(sourceFiles);
    console.time("transpile:createProgram");
    const program = ts.createProgram(Object.keys(sourceFiles), options, compilerHost);
    console.timeEnd("transpile:createProgram");
    const emit = async (sourceFile) => {
      return program.emit(sourceFile, writeFile, void 0, true, void 0, true);
    };
    console.time("transpile:emit");
    await Promise.all(
      Object.keys(sourceFiles).map((filename) => {
        return emit(sourceFiles[filename]);
      })
    );
    console.timeEnd("transpile:emit");
  }
  get repaceTo() {
    return isSingleDomain ? "@remote/" + this.domain : "@remote";
  }
  getModuleName(srcfullpath, fname) {
    let moduleName = fname.replace(srcfullpath, this.repaceTo);
    moduleName = moduleName.replace(/(\/index\.d\.ts|\.d\.ts)/, "");
    return moduleName;
  }
  toRemote(name, text) {
    const srcAliasSource = (0, import_tosrc.srcAliasPath)(name, text);
    return srcAliasSource.replace(/( declare |^declare )/gm, " ").replace(/src\/(.+)/gm, this.repaceTo + "/$1");
  }
  async installDts() {
    const cwd = process.cwd();
    const domain = this.domain;
    try {
      import_fs_extra.default.statSync(this.dtsFilePath);
    } catch (err) {
      return console.log(import_chalk.default.red(`
 添加失败,请先到[${domain}]域所在工程执行 cdts build ${domain} 
`));
    }
    const dest = import_path.default.resolve(cwd, "src/types", this.dtsFileName);
    await import_fs_extra.default.mkdirp(import_path.default.dirname(dest));
    await import_fs_extra.default.copyFile(this.dtsFilePath, dest);
    console.log(import_chalk.default.green(`
${dest} 添加成功 
`));
  }
  async writeFile(filename, output) {
    await import_fs_extra.default.mkdirp(import_path.default.dirname(filename));
    return import_fs_extra.default.writeFile(filename, output);
  }
  async buildDts() {
    console.time("total");
    const cwd = process.cwd();
    console.time("cache diff");
    const files = await this.diff(true);
    console.timeEnd("cache diff");
    console.log("diff count: " + import_chalk.default.yellow(files.length));
    if (files.length === 0)
      return console.log(import_chalk.default.green("\n" + this.dtsFilePath + " created successfull\n"));
    console.time("createSourceFiles");
    const sourceFiles = await this.createSourceFiles(files.map((it) => it[0]));
    console.timeEnd("createSourceFiles");
    const compilerOptions = {
      sourceMap: false,
      lib: ["DOM", "ScriptHost", "ESNext"],
      skipLibCheck: true
    };
    const srcfullpath = import_path.default.resolve(cwd, "src");
    const writeFile = (name, text) => {
      const remoteModule = this.getModuleName(srcfullpath, name);
      const dtsCode = `declare module "${remoteModule}" {
${this.toRemote(name, text)}}
`;
      this.updateDts(name.replace(/\.d\.ts$/, ".ts"), dtsCode);
    };
    console.time("transpile:total");
    await this.transpile(sourceFiles, writeFile, compilerOptions);
    console.timeEnd("transpile:total");
    console.time("toDTS");
    const output = this.save().toDTS();
    console.timeEnd("toDTS");
    await this.writeFile(this.dtsFilePath, output);
    console.timeEnd("total");
    console.log("");
    console.log(import_chalk.default.green("\n" + this.dtsFilePath + " created successfull\n"));
  }
};
let DtsHelper = _DtsHelper;
/** 缓存目录 */
__publicField(DtsHelper, "cacheDir", import_path.default.resolve(import_os.default.homedir(), ".dtscache"));
function showHelp() {
  process.stdout.write(process.platform === "win32" ? "\x1B[2J\x1B[0f" : "\x1B[2J\x1B[3J\x1B[H");
  console.log(`

  ${import_chalk.default.green("@chanjet/cjet-cmd")} ${import_chalk.default.yellow(import_package.version)} 
  
❓ 领域类型同步：

   cdts <add> domain
   
   ${import_chalk.default.yellow("cdts add apphub")}           install apphub.d.ts to src/types/apphub.d.ts
   ${import_chalk.default.yellow("cdts apphub")}               generate apphub.d.ts and save to ~/.dtscache/apphub.d.ts
   ${import_chalk.default.yellow("cdts components")}           generate components.d.ts and save to ~/.dtscache/components.d.ts
`);
}
async function main() {
  let [subcmd = "build", domain = process.env.CURRENT_DOMAIN] = cmdArgs;
  if (!["build", "add"].includes(subcmd)) {
    domain = subcmd;
    subcmd = "build";
  }
  const dtsHelper = new DtsHelper(domain);
  showHelp();
  if (subcmd === "add")
    return dtsHelper.installDts();
  dtsHelper.buildDts();
}
main();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  DtsHelper
});
